package pyip.lib.javalearn.socket;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * @Description: 测试Socket中，流关闭后，socket是否关闭？是否可重开流？输出缓存区的数据是发送出去，还是丢弃？
 *               TODO socket shutdown close 区别??
 */
public class SocketDemo {

    public static void main(String[] args) throws Exception {
        new SocketDemo();
    }

    public SocketDemo() throws Exception {
        // 输出流关闭的测试一：socket关闭吗？
        test1();
        // 避免Console的异常输出顺序混乱
        Thread.sleep(10);

        // 输出流关闭测试二：该流是否可以重新开启？
        test2();
        Thread.sleep(10);

        // 输出流关闭测试三：输出缓冲区里的数据是丢弃，还是发送？
        test3();
    }

    private void test1() {
        // 输出流关闭的测试一：socket关闭吗？
        System.out.println("\n****2 种方式关闭输出流，Socket是否关闭？***\n");

        Socket mySocket = null;
        DataOutputStream dout = null;

        try {
            mySocket = new Socket("127.0.0.1", 9999);
            dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));
            // 下面这一句主要是用来证明socket确实处于开启状态
            System.out.println("输出流刚打开，Socket isClosed？" + mySocket.isClosed());
            mySocket.shutdownOutput();
            System.out.println("使用Socket.shutdownOutput关闭输出流，Socket isClosed？" + mySocket.isClosed());
            System.out.println("使用Socket.shutdownOutput关闭输出流，Socket isInputShutdown？" + mySocket.isInputShutdown());
            System.out.println("使用Socket.shutdownOutput关闭输出流，Socket isOutputShutdown？" + mySocket.isOutputShutdown());
            dout.close();
            System.out.println("使用OutputStream.close关闭输出流，Socket isClosed？" + mySocket.isClosed());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (mySocket != null) {
                try {
                    mySocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (dout != null) {
                try {
                    dout.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    }

    private void test2() {
        // 输出流关闭测试二：使用shutdownOutputStream后，输出流是否可以重新开启？不能
        System.out.println("\n****使用shutdownOutputStream后，输出流是否可以重新开启？***\n");

        Socket mySocket = null;
        DataOutputStream dout = null;
        try {
            mySocket = new Socket("127.0.0.1", 9999);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        try {
            dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));
            mySocket.shutdownOutput();
            System.out.println("使用shutdownOutput关闭输出流，Socket isOutputShutdown？" + mySocket.isOutputShutdown());
            // 重开输出流
            dout = new DataOutputStream(mySocket.getOutputStream());
            System.out.println("重新开打输出流，Socket isOutputShutdown？" + mySocket.isOutputShutdown());
            dout.writeUTF("是否允许我重开？");
            // 清空输出缓存，确保当dout通道没问题时，消息可以到达服务器
            dout.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (mySocket != null) {
                try {
                    mySocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (dout != null) {
                try {
                    dout.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 在本方法中, 因为dout = new DataOutputStream(newBufferedOutputStream(mySocket.getOutputStream()));
     * 使用了Buffered，所以在dout.writeUTF()方法后，如果没有使用dout.flush(); 数据会存在输出缓存中，不会发送出去的。
     * 如果我们对dout的声明是 dout = new DataOutputStream(mySocket.getOutputStream()); 那么，数据会立即发送出去(阻塞)。
     */
    private void test3() {
        // 输出流关闭测试三：输出缓冲区里的数据是丢弃，还是发送？不发送, 且对方会得到一个 EOFException
        System.out.println("\n***输出缓冲区里的数据是丢弃，还是发送？****\n");

        Socket mySocket = null;
        DataOutputStream dout = null;
        try {
            mySocket = new Socket("127.0.0.1", 9999);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        try {
            dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));
            dout.writeUTF("shutdownOutput后，数据发得得出去吗？");
            // dout.flush(); 不flush就关闭, 发不出去, 且另一端会得到一个 EOFException
            mySocket.shutdownOutput();
            // dout.flush(); // shutdownOutput之后 flush会报错, 得到一个 SocketException
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (mySocket != null) {
                try {
                    mySocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (dout != null) {
                try {
                    dout.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}
